import random

from django.db.models import Q
from django.http import HttpResponse
from django.http import JsonResponse
from django.shortcuts import render
from django_redis import get_redis_connection
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.generics import GenericAPIView, UpdateAPIView, CreateAPIView, RetrieveAPIView
from rest_framework.mixins import RetrieveModelMixin, CreateModelMixin, UpdateModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet

from celery_tasks.sms.tasks import send_sms_code
from goods.models import SKU
from goods.serializers import SKUSerializer
from meiduomall.utils.captcha.captcha import captcha
from users import constants
from users.models import User, Address
from users.serializers import EmailSerializer, UserSerializer, UserDetailSerializer, AddressSerializer, TitleSerializer, \
    BrowseHistoriesSerializer, SavePasswordSerializer, VerifySmscodeSerializer, \
    UpdatePasswordSerializer

import logging
# 生成日志器
import logging

from verifications.constants import SMS_CODE_EXPIRES, SEND_SMS_CODE_INTERVAL

logger = logging.getLogger('django')
# GET /image_codes/(?P<pk>\d+)/
from users.utils import generate_find_password_accesstoken, decode_accesstoken


class GenerateImageCodeView(APIView):

    def get(self, request, pk):
        # 调用generate_captcha获取图片验证码编号，验证码，图片(二进制)
        name, text, image_data = captcha.generate_captcha()

        # 将图片存储到redis
        redis_conn = get_redis_connection('image_codes')
        redis_conn.set("image_code:%s" % pk, text, constants.IMAGE_CODE_REDIS_EXPIRES)

        return HttpResponse(image_data, content_type='image/png')


# GET /accounts/(?P<username>/w+)/sms/token/
class FindMobileView(APIView):

    def get(self, request, pk):

        # 获取参数
        try:
            user = User.objects.get(Q(username=pk) | Q(mobile=pk))
        except User.DoesNotExist:
            return Response({"message": "输入用户名有误"}, status=status.HTTP_404_NOT_FOUND)

        mobile = user.mobile

        image_code = request.query_params.get('text')
        image_code_id = request.query_params.get('image_code_id')

        # 拼接redis存储图片验证码的key值
        image_code_real_key = "image_code:%s" % image_code_id

        # 查询redis获取图片验证码， 并进行验证
        redis_conn = get_redis_connection('image_codes')
        image_code_real = redis_conn.get(image_code_real_key)
        if not image_code_real:
            return Response({"message": "图片验证码已过期"}, status=status.HTTP_400_BAD_REQUEST)

        if image_code_real.lower().decode() != image_code.lower():
            return Response({"message": "图片验证码输入有误"}, status=status.HTTP_400_BAD_REQUEST)

        redis_conn.delete(image_code_real_key)

        # 组织secret_mobile数据，返回前端
        secret_mobile = mobile[0: 2] + "****" + mobile[-5: -1]
        # 生成access_token，确保用户进行修改密码的一致性和有效期
        access_token = generate_find_password_accesstoken(mobile)

        return Response({
            "mobile": secret_mobile,
            "access_token": access_token
        }, status=status.HTTP_200_OK)


# GET /sms_codes/
class SMSCodeView(APIView):
    def get(self, request):
        # 获取参数access_token
        access_token = request.query_params.get('access_token', '')

        # 校验是否有access_token
        if not access_token:
            return Response({"message": "access_token缺失"}, status=status.HTTP_400_BAD_REQUEST)

        # 调用解密函数，对access_token进行解密
        mobile_dict = decode_accesstoken(access_token)

        # 解密失败
        if not mobile_dict:
            return Response({"message": "权限丢失"}, status=status.HTTP_400_BAD_REQUEST)

        mobile = mobile_dict['secret_key']

        # 检验解密之后的mobile
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            return Response({"message": "权限丢失"}, status=status.HTTP_401_UNAUTHORIZED)

        # 判断给'mobile'60s之内是否发过短信
        redis_conn = get_redis_connection('verify_codes')
        send_flag = redis_conn.get('send_flag_%s' % mobile)

        if send_flag:
            return Response({'message': '短信发送过于频繁'}, status=status.HTTP_403_FORBIDDEN)

        # 发出任务消息，使用celery异步任务队列，调worker发短信
        #  随机生成6位手机号
        sms_code = "%06d" % random.randint(0, 999999)
        logger.info('短信验证码的内容是：%s' % sms_code)

        # 创建redis-pipeline对象
        pl = redis_conn.pipeline()

        # 向redis管道中添加命令
        pl.setex('sms_%s' % mobile, SMS_CODE_EXPIRES, sms_code)
        pl.setex('send_flag_%s' % mobile, SEND_SMS_CODE_INTERVAL, sms_code)

        # 一次性执行管道中的所有命令
        pl.execute()

        # 组织参数过期时间
        expires = SMS_CODE_EXPIRES // 60

        # 发出任务消息
        send_sms_code.delay(mobile, sms_code, expires)

        # 返回响应
        return Response({"message": "OK"})


# GET /accounts/(?P<username>\w+)/password/token/
class VerifySmscodeView(GenericAPIView):
    """短信验证码校验"""
    serializer_class = VerifySmscodeSerializer
    queryset = User.objects.all()

    # def get_objest(self, username):
    #     # 重写get_object方法，返回用户对象
    #     user = User.objects.get(username=username)
    #     return user

    def get(self, request, username):

        # 获取参数
        sms_code = request.query_params.get("sms_code")
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username))
        except User.DoesNotExist:
            return Response({"message": "用户名有误"})

        data = {
            'sms_code': sms_code,
            'user_id': user.id,
            'username': user.username
        }

        # 生成序列化器
        serializer = self.get_serializer(data=data)

        # 数据校验
        serializer.is_valid(raise_exception=True)

        # 数据保存
        serializer.save()

        # # 获取参数，sms_code
        # sms_code = request.query_params.get('sms_code', '')
        # if not sms_code:
        #     return Response({"message": "OK"}, status=status.HTTP_400_BAD_REQUEST)

        # 根据username取到user对象，根据user对象的mobile获取redis中的手机号验证码(bytes)
        # try:
        #     user = User.objects.get(username=username)
        # except User.DoesNotExit:
        #     return Response({"message": "用户名有误"}, status=status.HTTP_400_BAD_REQUEST)
        #
        # sms_code_redis_key = "sms_%s" %user.mobile
        #
        # redis_conn = get_redis_connection('verify_codes')
        # sms_code_redis = redis_conn.get(sms_code_redis_key)
        #
        # if not sms_code_redis:
        #     return Response({'message': '验证码已过期'}, status=status.HTTP_408_REQUEST_TIMEOUT)
        #
        # # 比较手机验证码是否正确
        # if sms_code_redis.decode() != sms_code:
        #     return Response({'message': '验证码输入有误'}, status=status.HTTP_400_BAD_REQUEST)

        # # 组织响应数据返回{"user_id": "", "access_token": ""}
        # access_token = generate_find_password_accesstoken(username)

        return Response(serializer.data)


# POST /users/(?P<pk>\d+)/password/
class SavePasswordView(GenericAPIView):
    # def perform_authentication(self, request):
    #     """让当前视图跳转DRF框架默认认证过程"""
    #     pass
    # permission_classes = [IsAuthenticated]
    serializer_class = SavePasswordSerializer
    queryset = User.objects.all()

    # def perform_authentication(self, request):
    #     """重写父类perform_authentication，让当前视图跳过DRF框架认证过程"""
    #     pass

    def post(self, request, pk):
        data = request.data

        # 给data添加pk字段，进行用户校验
        data['pk'] = pk

        # 获取用户对象
        user = self.get_object()

        serializer = self.get_serializer(user, data=data)
        # 数据校验
        serializer.is_valid(raise_exception=True)

        # 数据保存
        serializer.save()  # update

        # 获取参数
        # access_token = request.data.get('access_token')
        # password = request.data.get('password')
        # password2 = request.data.get('password2')
        #
        # if not all([access_token, password, password2]):
        #     return Response({"message": "OK"}, status=status.HTTP_400_BAD_REQUEST)

        # 校验access_token
        # username_dict = decode_accesstoken(access_token)
        # if not username_dict:
        #     return Response({"message": "权限丢失"})
        #
        # username = username_dict['secret_key']
        #
        # try:
        #     user = User.objects.get(username=username)
        # except User.DoesNotExist:
        #     return Response({"message": "权限丢失"}, status=status.HTTP_401_UNAUTHORIZED)
        #
        # try:
        #     user2 = User.objects.get(id=pk)
        # except User.DoesNotExist:
        #     return Response({"message": "权限丢失"}, status=status.HTTP_401_UNAUTHORIZED)
        #
        # if user2.id != user.id:
        #     return Response({"message": "操作用户不一致"}, status=status.HTTP_409_CONFLICT)

        # if password2 != password:
        #     return Response({"message": "两次输入密码不一致"}, status=status.HTTP_400_BAD_REQUEST)

        # user.set_password(password)
        # user.save()

        return Response({"message": "OK"})

    def put(self, request, pk):
        """
        PUT /users/(?P<pk>\d+)/password/
        request.user:
            如果用户已登录，request.user就是登录用户对象
            如果用户未登录，request.user就是一个匿名用户类对象
        """
        user = request.user
        is_login = user.is_authenticated  # 登录返回True,未登录返回False

        if not is_login:
            return Response({'message': '用户未认证'}, status=status.HTTP_401_UNAUTHORIZED)

        # 1. 获取参数并进行校验(id是否正确, 密码是否正确, 两次密码是否一致)
        # user = request.user
        request.data['user_id'] = pk

        serializer = UpdatePasswordSerializer(data=request.data, context={'user': user})
        serializer.is_valid(raise_exception=True)

        # 2. 更新用户并保存到数据库create
        serializer.save()

        # 3. 修改成功, 返回响应
        return Response({"message": "OK"}, status=status.HTTP_201_CREATED)


# PUT /user/(?P<user_id>\d+)/password/（改前端接口）
class UpdatePasswordView(GenericAPIView):
    serializer_class = UpdatePasswordSerializer
    permission_classes = [IsAuthenticated]

    def put(self, request, user_id):
        # 1. 获取参数并进行校验(id是否正确, 密码是否正确, 两次密码是否一致)
        # user = request.user
        request.data['user_id'] = user_id
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 更新用户并保存到数据库create
        serializer.save()

        # 3. 修改成功, 返回响应
        return Response({"message": "OK"}, status=status.HTTP_201_CREATED)


# Create your views here.
# POST /browse_histories/
class BrowseHistoriesView(CreateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = BrowseHistoriesSerializer

    # def post(self, request):
    #     # 1.获取参数并进行校验
    #     serializer = self.get_serializer(data=request.data)
    #     serializer.is_valid(raise_exception = True)
    #
    #     # 2.将浏览记录添加到redis的list中(create)
    #     serializer.save()
    #     # 3.返回响应
    #     return Response(serializer.data)
    def get(self, request):
        # 获取用户登录的浏览记录
        # 获取用户的id,组织redis记录的key
        redis_coon = get_redis_connection('histories')
        user = request.user
        redis_key = 'history_%s' % user.id

        # 获取redis中的浏览记录sku_id
        sku_ids = redis_coon.lrange(redis_key, 0, -1)
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append(sku)

        # 使用序列化器进行序列化并返回响应
        serializer = SKUSerializer(skus, many=True)

        return Response(serializer.data)


class AddressViewSet(CreateModelMixin, UpdateModelMixin, GenericViewSet):
    permission_classes = [IsAuthenticated]
    serializer_class = AddressSerializer

    def get_queryset(self):

        return self.request.user.addresses.filter(is_deleted=False)

    # GET /addresses/
    def list(self,request,):

        addresses = self.get_queryset()

        serializer = self.get_serializer(addresses, many=True)

        user = request.user
        return Response({
            'user_id': user.id,
            'default_address_id': user.default_address_id,
            'limit': constants.USER_ADDRESS_COUNT_LIMIT,
            'addresses': serializer.data

        })

    # DELETE /addresses/(?p<pk>\d+)
    def destroy(self, request, pk):
        # 1.根据pk获取指定的地址
        address = self.get_object()
        # 2.将地址的is_deleted属性改为True
        address.is_deleted = True
        address.save()
        # 3.返回应答204
        return Response(status=status.HTTP_204_NO_CONTENT)

    # UPDATE /address/(?P<pk>\d+)
    # def update(self, request, pk):
    #     # 1.根据pk获取地址对象
    #     address = self.get_object()
    #
    #     # 2.获取参数并进行校验
    #     serializer = self.get_serializer(data=request.data)
    #     serializer.is_valid()
    #
    #     # 3.修改指定地址的数据
    #     serializer.save()
    #
    #     # 4.返回序列化之后的地址数据
    #     return Response(serializer.data)

    # POST /addresses/
    def create(self, request, *args, **kwargs):
        """
        登录用户地址的新增
        1.获取参数并进行校验(参数完整性，手机号格式是否正确，邮箱格式)
        2.创建并保存新增地址数据
        3.将新增地址数据序列化并返回
        """
        # 0.判断用户的地址是否超过上限
        count = request.user.addresses.filter(is_deleted=False).count()

        if count >= constants.USER_ADDRESS_COUNT_LIMIT:
            return Response({'message': '地址数量超过上限'}, status=status.HTTP_400_BAD_REQUEST)
        # # 1.获取参数并进行校验(参数完整性，手机号格式是否正确，邮箱格式)
        # serializer = self.get_serializer(data=request.data)
        # serializer.is_valid(raise_exception=True)
        #
        # # 2.创建并保存新增地址数据(create)
        # serializer.save()
        #
        # # 3.将新增地址数据序列化并返回
        # return Response(serializer.data, status=status.HTTP_201_CREATED)

        # 调用CreateModelMixin中的create()方法
        return super().create(request)

    # PUT /addresses/status/(?P<pk>\d+)
    @action(methods=['put'], detail=True)
    def status(self, request, pk):
        # 1.根据pk获取地址信息
        address = self.get_object()
        if not address:
            return Response({'message': '地址id有误'}, status=status.HTTP_400_BAD_REQUEST)
        # 2.修改用户默认地址
        user = request.user
        user.default_address_id = address.id
        user.save()
        # 3.返回响应，修改成功
        return Response({'message': 'OK'})

    # PUT /addresses/(?P<pk>\d+)/title
    @action(methods=['put'], detail=True)
    def title(self, request, pk):
        # 1.根据pk获取指定的address数据
        address = self.get_object()
        # 2.获取数据并进行校验
        serializer = TitleSerializer(address, data=request.data)
        serializer.is_valid()

        # 3.修改地址的title数据
        serializer.save()
        # 4.返回响应，修改成功
        return Response(serializer.data)


# PUT /emails/verification/?token=<加密用户的信息>
class EmailVerifyView(APIView):
    def put(self, request):
        """
        用户邮箱验证：
        1.获取token并进行校验(token必传，token是否有效)
        2.设置对应用户的邮箱验证标记email_active为True
        3.返回应答，验证成功
        """
        token = request.query_params.get('token')

        if token is None:
            return Response({'message': '缺少token参数'}, status=status.HTTP_400_BAD_REQUEST)

        user = User.check_email_verify_token(token)

        if user is None:
            return Response({'message': '无效的token'}, status=status.HTTP_400_BAD_REQUEST)

        user.email_active = True
        user.save()

        return Response({'message', 'OK'})


# PUT /email/
# class EmailView(GenericAPIView):
class EmailView(UpdateAPIView):
    # 当前视图的权限设置，只允许通过认证的用户访问
    permission_classes = [IsAuthenticated]
    # 指定当前视图所使用的序列化器类
    serializer_class = EmailSerializer

    # def put(self, request):
    #     """
    #     1.获取参数并进行校验(email必传，email格式)
    #     2.设置登录用户的邮箱并给邮箱发送验证邮件
    #     3.返回应答，邮箱设置成功
    #     """
    #     # 获取登录用户
    #     user = request.user
    #     # 1. 获取参数并进行校验(email必传，email格式)
    #     serializer = self.get_serializer(user, data=request.data)
    #     # 使用is_valid对参数进行校验，设置raise_exception=True，如果数据未通过验证，DRF框架将会自动抛出异常
    #     serializer.is_valid(raise_exception=True)
    #
    #     # 2.设置登录用户的邮箱并给邮箱发送验证邮件(update)(传了instance和dataDRF框架会自动调用update方法更新对象数据)
    #     serializer.save()
    def get_object(self):
        """返回用户登录对象"""
        return self.request.user

    #
    #     # 3.返回应答，邮箱设置成功
    #     return Response(serializer.data)


# GET /user/
class UserDetailView(RetrieveAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = UserDetailSerializer

    def get_object(self):
        """返回登录用户对象"""

        return self.request.user

    # def get(self, request):
    #     """
    #     获取登录用户基本信息
    #     1.获取登录用户
    #     2.将获取到的用户序列化并返回
    #     """
    #     # 获取登录用户
    #     user = request.user
    #     # 将获取到的用户序列化并返回
    #     serializer = self.get_serializer(user)
    #
    #     return Response(serializer.data)


# POST /users/
class UserView(CreateAPIView):
    serializer_class = UserSerializer

    # def post(self, request):
    #     """
    #     注册用户信息的保存：
    #     1.获取参数并进行校验(参数完整性，是否同意协议，手机号格式，手机号是否存在，两次密码是否一致，验证码是否正确有效)
    #     2.创建新用户并保存到数据库
    #     3.注册成功，将新用户序列化并返回
    #     """
    #     # 1.获取参数并进行校验(参数完整性，是否同意协议，手机号格式，手机号是否存在，两次密码是否一致，验证码是否正确有效)
    #     serializer = self.get_serializer(data=request.data)
    #     serializer.is_valid(raise_exception=True)
    #     # 2.创建新用户并保存到数据库(create)
    #     serializer.save()
    #     # 3.注册成功，将新用户序列化并返回
    #     return Response(serializer.data, status=status.HTTP_201_CREATED)


# GET usernames/(?P<username>\w{5,20})/count/
class UserNameView(APIView):
    def get(self, request, username):
        """查询数据库以返回该用户名的数量"""
        count = User.objects.filter(username=username).count()
        res_dict = {
            'username': username,
            'count': count
        }
        return Response(res_dict)


# 判断手机号是否已存在
# GET mobiles/(?P<mobile>1[3-9]\d{9})/count/
class MobileNumbersView(APIView):
    def get(self, request, mobile):
        """查询数据库对应手机号有几条并返回响应"""
        count = User.objects.filter(mobile=mobile).count()
        res_dict = {
            'mobile': mobile,
            'count': count,
        }
        return Response(res_dict)